package com.indoors.chart;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.HashMap;

/**
 * Created by indoor31 on 2018/4/10.
 */

public class RelationChartLayout extends ViewGroup {
    private Paint mPaint;

    private static final float DEFAULT_MIN_LINE_SIZE = 1;
    private static final float DEFAULT_MAX_LINE_SIZE = 3;
    private static final float DEFAULT_MIN_LEAF_SIZE = 0.4f;
    private static final float DEFAULT_MAX_LEAF_SIZE = 1f;

    private boolean hasTitle = true;
    private boolean hasLine = true;
    private float minLineSize;
    private float maxLineSize;
    private float minDistance;
    private float minLeafSize;
    private float maxLeafSize;
    private View title;
    private HashMap<View, int[]> locations;
    private ChartAdapter adapter;
    private int lineColor = Color.parseColor("#d2d2d2");

    public RelationChartLayout(Context context) {
        super(context);
        init();
    }

    public RelationChartLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
        init();
    }

    public RelationChartLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
        init();
    }

    private void init(AttributeSet attrs) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.RelationChartLayout);
        //获取设置在子控件上的位置属性
        hasTitle = a.getBoolean(R.styleable.RelationChartLayout_has_center, hasTitle);
        hasLine = a.getBoolean(R.styleable.RelationChartLayout_has_line, hasLine);
        lineColor = a.getInt(R.styleable.RelationChartLayout_line_color, lineColor);
        minLineSize = a.getDimension(R.styleable.RelationChartLayout_line_min_size,
                dip2px(DEFAULT_MIN_LINE_SIZE));
        maxLineSize = a.getDimension(R.styleable.RelationChartLayout_line_max_size,
                dip2px(DEFAULT_MAX_LINE_SIZE));
        minDistance = a.getFloat(R.styleable.RelationChartLayout_leaf_min_distance,
                LayoutParams.DEFAULT_DISTANCE / 4);
        minLeafSize = a.getFloat(R.styleable.RelationChartLayout_leaf_min_size,
                DEFAULT_MIN_LEAF_SIZE);
        maxLeafSize = a.getFloat(R.styleable.RelationChartLayout_leaf_max_size,
                DEFAULT_MAX_LEAF_SIZE);
        a.recycle();
    }

    private void init() {
        if (getBackground() == null) {
            setBackgroundColor(Color.parseColor("#00000000"));
        }
        locations = new HashMap<>();
        mPaint = new Paint();
        mPaint.setColor(lineColor);
        mPaint.setAntiAlias(true);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        int wms = widthMeasureSpec;
        if (mode == MeasureSpec.UNSPECIFIED) {
            wms = widthMeasureSpec | MeasureSpec.AT_MOST;
        }
        super.onMeasure(wms, heightMeasureSpec);
        mode = MeasureSpec.getMode(heightMeasureSpec);
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
            height = width;
        }
        // 计算出所有的childView的宽和高
        measureChildren(width, height);
        setMeasuredDimension(width, height);
    }

    @Override
    protected void measureChildren(int width, int height) {
        if (adapter != null) {
            adapter.createView(this);
        }
        int size = getChildCount();
        double angle = 0;
        double titleHeight = height * 0.1;
        float leafRange = maxLeafSize - minLeafSize;
        double minDistanceSize = (height * minDistance + maxLeafSize * titleHeight) / 2;
        double maxDistanceSize = (height - maxLeafSize * titleHeight) / 2;
        for (int i = 0; i < size; ++i) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            if ((lp.type == LayoutParams.TITLE_CHILD) && hasTitle) {
                title = child;
                if (lp.weight == LayoutParams.DEFAULT_WEIGHT && Math.max(lp.width, lp.height) > 0) {
                    if (Math.min(lp.width, lp.height) <= 0) {
                        titleHeight = Math.max(lp.width, lp.height);
                    } else {
                        titleHeight = Math.min(lp.width, lp.height);
                    }
                } else {
                    titleHeight = titleHeight * lp.weight;
                }
                minDistanceSize = (height * minDistance + maxLeafSize * titleHeight) / 2;
                maxDistanceSize = (height - maxLeafSize * titleHeight) / 2;
                int sizeMeasureSpec = MeasureSpec.makeMeasureSpec(
                        (int) (titleHeight + 0.5), MeasureSpec.EXACTLY);
                child.measure(sizeMeasureSpec, sizeMeasureSpec);
                titleHeight = child.getMeasuredHeight();
                break;
            }
        }
        // 计算各个控件的大小，以及占用的角度大小
        for (int i = 0; i < size; ++i) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE || child == title) {
                continue;
            }
            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            double itemHeight = titleHeight * (leafRange * lp.weight + minLeafSize);
            double distance = (maxDistanceSize - minDistanceSize) * lp.distance + minDistanceSize;
            // 求圆外一点，到内正方形边长为itemHeight的圆的圆心与正切边的夹角, 圆心到点的距离为distance
            double r = itemHeight / Math.sqrt(2);
            lp.distanceSize = distance;
            lp.radius = r;
            angle += Math.asin(r / distance) * (180 / Math.PI);
        }
        // 位置有余加大间距，位置不够就缩小叶子
        double padding = 0;
        double scale = 1;
        if (angle < 360) {
            if (title != null) {
                padding = (360 - angle) / (size - 1);
            } else {
                padding = (360 - angle) / size;
            }
        } else if (angle >= 360 && angle <= 540) { // 不能缩太小
            scale = angle / 360;
        } else {
            scale = 1.5;
        }
        double totalAngle = 0;
        double itemAngle = 0;
        for (int i = 0; i < size; ++i) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE || child == title) {
                continue;
            }
            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            lp.radius = lp.radius * scale;
            itemAngle = Math.asin(lp.radius / lp.distanceSize) * (180 / Math.PI);
            lp.angle = totalAngle + itemAngle / 2;
            totalAngle = totalAngle + itemAngle + padding;
            int sizeMeasureSpec = MeasureSpec.makeMeasureSpec(
                    (int) (lp.radius * Math.sqrt(2) + 0.5), MeasureSpec.EXACTLY);
            child.measure(sizeMeasureSpec, sizeMeasureSpec);
        }
    }

    /**
     * 为所有的子控件摆放位置.
     */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int centerX = (right - left) / 2;
        int centerY = (bottom - top) / 2;
        if (title != null && hasTitle) {
            layoutChild(title, centerX, centerY);
        }
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            if (child == title) {
                continue;
            }
            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            double itemCenterX = centerX + lp.distanceSize * Math.sin(lp.angle * Math.PI / 180);
            double itemCenterY = centerX - lp.distanceSize * Math.cos(lp.angle * Math.PI / 180);

            layoutChild(child, (int) (itemCenterX + 0.5), (int) (itemCenterY + 0.5));
        }
    }

    private void layoutChild(View v, int x, int y) {
        int l = x - v.getMeasuredWidth() / 2;
        int r = l + v.getMeasuredWidth();
        int t = y - v.getMeasuredHeight() / 2;
        int b = t + v.getMeasuredHeight();
        v.layout(l, t, r, b);
        locations.put(v, new int[]{x, y});
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (title == null || !hasLine) {
            return;
        }
        int[] center = locations.get(title);
        for (int i = 0; i < getChildCount(); i++) {
            View c = getChildAt(i);
            if (c == title) {
                continue;
            }
            int[] loc = locations.get(c);
            LayoutParams lp = (LayoutParams) c.getLayoutParams();
            float line = (maxLineSize - minLineSize) * lp.relation / 2 + minLineSize;
            mPaint.setStrokeWidth(line);
            canvas.drawLine(center[0], center[1], loc[0], loc[1], mPaint);
        }
    }

    public void setAdapter(ChartAdapter a) {
        if (adapter != a && adapter != null) {
            adapter.destroyViews(this);
        }
        adapter = a;
        adapter.createView(this);
    }

    public static class LayoutParams extends ViewGroup.LayoutParams {

        public static final int LEAF_CHILD = 0; // 叶子结点
        public static final int TITLE_CHILD = 1; // 根结点

        public static final float DEFAULT_WEIGHT = 0.9f; // 根结点
        public static final float DEFAULT_DISTANCE = 0.7f; // 根结点
        public static final float DEFAULT_RELATION = 0.5f; // 根结点

        public int type = LEAF_CHILD;  // 默认是叶子结点
        public float weight;    // 决定叶子的大小
        public float distance;  // 决定线的长短
        public float relation;  // 决定线的粗细
        public double distanceSize;
        public double radius;
        public double angle;

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.RelationChartParam);
            //获取设置在子控件上的位置属性
            type = a.getInt(R.styleable.RelationChartParam_chart_type, type);
            weight = a.getFloat(R.styleable.RelationChartParam_chart_weight, DEFAULT_WEIGHT);
            distance = a.getFloat(R.styleable.RelationChartParam_chart_distance, DEFAULT_DISTANCE);
            relation = a.getFloat(R.styleable.RelationChartParam_chart_relation, DEFAULT_RELATION);
            a.recycle();
        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }

        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
            if (source instanceof LayoutParams) {
                LayoutParams self = (LayoutParams) source;
                type = self.type;
                weight = self.weight;
                distance = self.distance;
                relation = self.relation;
            }
        }
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public float dip2px(float dpValue) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public static abstract class ChartAdapter {

        private ArrayList<View> views;

        private boolean isCreateView() {
            return views != null && !views.isEmpty();
        }

        private void createView(ViewGroup parent) {
            int size = getCount();
            if (views == null) {
                views = new ArrayList<>();
            }
            destroyViews(parent);
            for (int i = 0; i < size; i++) {
                View v = getView(i);
                views.add(v);
                if (v.getLayoutParams() instanceof LayoutParams) {
                    parent.addView(v);
                    continue;
                }
                LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                ViewGroup.LayoutParams vlp = v.getLayoutParams();
                if (vlp != null) {
                    lp = new LayoutParams(vlp);
                }
                lp.weight = getItemWeight(i);
                lp.distance = getItemDistance(i);
                lp.relation = getItemRelation(i);
                v.setLayoutParams(lp);
                parent.addView(v);
            }
            parent.invalidate();
        }

        private void destroyViews(ViewGroup parent) {
            if (isCreateView()) {
                for (View v : views) {
                    parent.removeView(v);
                }
                parent.invalidate();
            }
        }

        protected float getItemRelation(int position) {
            return 1;
        }

        protected abstract float getItemDistance(int position);

        protected abstract float getItemWeight(int position);

        public abstract int getCount();

        protected abstract View getView(int position);
    }
}
